/**
 * 
 */
package org.intelligentsia.artifacts.repository;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import com.ning.http.client.AsyncHandler;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.AsyncHttpClientConfig.Builder;
import com.ning.http.client.HttpResponseBodyPart;
import com.ning.http.client.HttpResponseHeaders;
import com.ning.http.client.HttpResponseStatus;
import com.ning.http.client.Response;
import com.ning.http.client.resumable.ResumableAsyncHandler;
import com.ning.http.client.resumable.ResumableListener;
import com.ning.http.util.AsyncHttpProviderUtils;

/**
 * @author geronimo
 * 
 */
public class HttpRepository {
	private Locator<String>	locator;
	AsyncHttpClient			asyncHttpClient;

	/**
	 * Build a new instance of <code>HttpRepository</code>
	 * 
	 * @param repository
	 * @throws MalformedURLException
	 */
	public HttpRepository(Repository repository) {
		super();
		locator = new URLLocator(AsyncHttpProviderUtils.createUri(repository.getUrl()).toString());
		Builder builder = new AsyncHttpClientConfig.Builder();
		builder.setCompressionEnabled(true).setAllowPoolingConnection(true).setRequestTimeoutInMs(30000).build();
		asyncHttpClient = new AsyncHttpClient(builder.build());
	}

	public File get(String resource) throws IOException {
		File destination = File.createTempFile("repository", ".tmp");
		// use a resumable download
		// from http://jfarcand.wordpress.com/2011/01/04/going-asynchronous-using-asynchttpclient-the-complex/
		final RandomAccessFile file = new RandomAccessFile(destination, "rw");
		final ResumableAsyncHandler<Response> handler = new ResumableAsyncHandler<Response>();
		handler.setResumableListener(new ResumableListener() {
			public void onBytesReceived(ByteBuffer byteBuffer) throws IOException {
				file.seek(file.length());
				file.write(byteBuffer.array());
			}

			public void onAllBytesReceived() {
				try {
					file.close();
				} catch (IOException e) {
					handler.onThrowable(e);
				}
			}

			public long length() {
				try {
					return file.length();
				} catch (IOException e) {
					handler.onThrowable(e);
					return -1;
				}
			}
		});
		Response response = null;
		try {
			response = asyncHttpClient.prepareGet(locator.resolve(resource)).execute(handler).get();
			if (response.getStatusCode() != 200) {
				destination.delete();
				throw new IllegalStateException("response code not 200 OK");
			}
		} catch (InterruptedException e) {
			destination.delete();
			throw new IllegalStateException(e);
		} catch (ExecutionException e) {
			destination.delete();
			throw new IllegalStateException(e);
		}

		return destination;
	}

	public boolean exists(String resource) throws IOException, InterruptedException, ExecutionException {
		Future<Boolean> f = asyncHttpClient.prepareGet(locator.resolve(resource)).execute(new AsyncHandler<Boolean>() {
			Boolean	result	= Boolean.FALSE;

			@Override
			public void onThrowable(Throwable t) {
				result = Boolean.FALSE;
			}

			@Override
			public com.ning.http.client.AsyncHandler.STATE onBodyPartReceived(HttpResponseBodyPart bodyPart)
					throws Exception {
				return STATE.ABORT;
			}

			@Override
			public com.ning.http.client.AsyncHandler.STATE onStatusReceived(HttpResponseStatus responseStatus)
					throws Exception {
				result = (responseStatus.getStatusCode() == 200);
				return STATE.ABORT;
			}

			@Override
			public com.ning.http.client.AsyncHandler.STATE onHeadersReceived(HttpResponseHeaders headers)
					throws Exception {
				return STATE.ABORT;
			}

			@Override
			public Boolean onCompleted() throws Exception {
				return result;
			}
		});
		return f.get();
	}

	public void put(String resource, File source) throws IOException {
		// RequestBuilder builder = new RequestBuilder("PUT");
		// Request request = builder.setUrl(locator.resolve(resource)).addHeader("name",
		// "value").setBody(source).build();
		Response response = null;
		try {
			response = asyncHttpClient.preparePut(locator.resolve(resource)).setBody(source).execute().get();
			if (response.getStatusCode() != 200) {
				throw new IllegalStateException("response code not 200 OK");
			}
		} catch (InterruptedException e) { 
			throw new IllegalStateException(e);
		} catch (ExecutionException e) { 
			throw new IllegalStateException(e);
		}
	}

	/**
	 * Inner locator.
	 * 
	 * @author geronimo
	 * 
	 */
	private class URLLocator implements Locator<String> {

		private String	root;

		/**
		 * Build a new instance of <code>URLLocator</code>
		 * 
		 * @param root
		 */
		public URLLocator(String root) {
			super();
			this.root = root.endsWith("/") ? root : root + "/";
		}

		/**
		 * @see org.intelligentsia.artifacts.repository.Locator#getRoot()
		 */
		@Override
		public String getRoot() {
			return root;
		}

		/**
		 * @see org.intelligentsia.artifacts.repository.Locator#resolve(java.lang.String)
		 */
		@Override
		public String resolve(String target) {
			return new StringBuilder(root).append(target).toString();
		}

	}

}
